/*
 * Copyright (c) 2025 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <plat/config.h>
#include <core/resource.h>
#include <tools/list.h>
#include <stdlib/stdint.h>
#include <core/task.h>
#include <core/schedule.h>
#include <core/pbuddy.h>
#include <core/dprintf.h>
#include <core/mutex.h>
#include <core/cond.h>
#include <core/notification.h>
#include <core/signal.h>
#include <uapi/errors.h>

/**
 * @brief   initialize a resource
 *
 * @param   res     pointer to the resource structure to be initialized
 * @param   ptr     pointer to the associated data
 * @param   type    type identifier for the resource
 */
void resource_init(resource_t *res, void *ptr, res_type_t type)
{
    res->s_type  = type;
    res->s_ptr   = ptr;
    res->s_extra = 0;
    res->s_owner = NULL;
    list_init(&res->s_list);
    list_init(&res->s_hlist);
}

/**
 * @brief   set the owner of the resource and update its position in the task's hold list
 *
 * @param   res     pointer to the resource to be updated
 * @param   task    pointer to the task that will become the new owner of the resource
 */
void resource_set_holder(resource_t *res, tcb_t *task)
{
    res->s_owner = task;

    list_remove(&res->s_list);
    list_append(&res->s_list, &task->holdlist);
}

/**
 * @brief   brief   add the resource to the endpoint's waitlist based on priority
 *
 * @param   res     pointer to the resource to be added
 * @param   ptr     pointer to the endpoint whose waitlist will be updated
 */
static void ep_waitlist_add_resource(resource_t *res, void *ptr)
{
    resource_t const *cur = NULL;
    list_t           *pos = NULL;

    if (list_is_empty(&((endpoint_t *)ptr)->waitlist) == 0) {
        list_foreach((cur), &((endpoint_t *)ptr)->waitlist, resource_t, s_list)
        {
            if (cur->s_owner->priority >= res->s_owner->priority) {
                pos = cur->s_list.prev;
                list_insert(&res->s_list, pos);
                break;
            }
        }
    } else {
        list_insert(&res->s_list, &((endpoint_t *)ptr)->waitlist);
    }
}

/**
 * @brief insert a node into the list while maintaining the list's order.
 *
 * @param list pointer to the head of the list.
 * @param res  pointer to the resource node to be inserted.
 */
void resource_prilist_add(list_t *list, resource_t *res)
{
    if ((list == NULL) || (res == NULL)) {
        return;
    }
    if ((list_is_empty(list) != 0) || (RES_PRIORITY((list->next)) > RES_PRIORITY((res)))) {
        // the new node become the new head node
        list_insert(&res->s_list, list);
    } else {
        list_t *cur = list->next;
        while (((cur->next) != list) && (RES_PRIORITY((cur)) <= RES_PRIORITY((res)))) {
            cur = cur->next;
        }
        local_list_add(&res->s_list, cur, cur->next);
    }
}

/**
 * @brief get the TCB with the highest priority value.
 *
 * @param  list    pointer to the head of the list.
 * @return tcb_t*  pointer to the TCB with the highest priority value.
 */
tcb_t *resource_prilist_first(list_t *list)
{
    // if the list pointer is null or no node in it, return NULL
    if ((list == NULL) || (list_is_empty(list) != 0)) {
        return NULL;
    }
    return (tcb_t *)(((resource_t *)list->next)->s_owner);
}

/**
 * @brief  get the TCB with the second highest priority value.
 *
 * @param  list   pointer to the head of the list.
 * @return tcb_t* pointer to the TCB with the second highest priority value.
 */
tcb_t *resource_prilist_next(list_t *list)
{
    // if the list pointer is null or no node in it, return NULL
    if ((list == NULL) || (list_is_empty(list) != 0) || (list->next->next == list)) {
        return NULL;
    }
    return (tcb_t *)(((resource_t *)list->next->next)->s_owner);
}

/**
 * @brief   add the resource to the appropriate waitlist based on its type
 *
 * @param   res     pointer to the resource to be added to the waitlist
 * @param   ptr     pointer to the object whose waitlist the resource will be added to
 */
void resource_attach_waitlist(resource_t *res, void *ptr)
{
    switch (res->s_type) {
        case RESOURCE_MUTEX:
            resource_prilist_add(&((mutex_t *)ptr)->waitlist, res);
            break;
        case RESOURCE_COND:
            resource_prilist_add(&((cond_t *)ptr)->waitlist, res);
            break;
        case RESOURCE_NTFN:
            list_append(&res->s_list, &((notification_t *)ptr)->waitlist);
            break;
        case RESOURCE_TASK:
            list_append(&res->s_list, &((tcb_t *)ptr)->waitlist);
            break;
        case RESOURCE_ENDPONIT:
            ep_waitlist_add_resource(res, ptr);
            break;
        case RESOURCE_SIG:
            list_append(&res->s_list, &((sig_list_t *)ptr)->waitlist);
            break;
        default:
            // default is empty
            break;
    }
    return;
}

/**
 * @brief   wake up the task after a timeout and unblock the other tasks waiting
 *          on the same resource
 *
 * @param   task     the task to wake up after a timeout
 */
void resource_timeout_wake(tcb_t *task)
{
    resource_t const *res = NULL;

    /**
     * @brief   if the task is not blocked on any resource, return early
     *
     */
    if (task->t_blockres == NULL) {
        return;
    }

    res = task->t_blockres;
    switch (res->s_type) {
        case RESOURCE_MUTEX:

            /**
             * @brief   wake up the task blocked by the mutex
             *
             */
            mutex_force_wakeup(task, ETIMEDOUT);
            break;
        case RESOURCE_COND:
            cond_force_wakeup(task, ETIMEDOUT);
            break;
        case RESOURCE_SIG:
            sig_timeout_wake(task);
            break;
        default:
            // default is empty
            break;
    }
}

/**
 * @brief   release the resources held by the task when it is terminated
 *
 * @param   task    pointer to the task to be terminated
 */
void task_delete_resource_deal(tcb_t *task)
{
    resource_t *res      = NULL;
    resource_t *pos      = NULL;
    resource_t *pos_next = NULL;

    list_foreach_safe((pos), (pos_next), &task->holdlist, resource_t, s_list)
    {
        switch (pos->s_type) {
            case RESOURCE_MUTEX:
                task_exit_operate_mutex(task, pos);
                break;
            case RESOURCE_COND:    // COND is not held
                break;
            case RESOURCE_NTFN:    // NTFN is not held
                break;
            case RESOURCE_TASK:    // THREAD is not held
                break;
            default:
                // default is empty
                break;
        }
    }

    /**
     * @brief   it is necessary to remove the blocking source when a blocked task is deleted
     *
     */
    if (task->t_blockres != NULL) {
        res = task->t_blockres;
        list_remove(&res->s_list);
    }
}
